package com.youlu.campus.service.attendance.impl;

import com.alibaba.fastjson.JSON;
import com.youlu.campus.common.utils.TimeUtils;
import com.youlu.campus.entity.*;
import com.youlu.campus.entity.VO.RankingListQueryVO;
import com.youlu.campus.entity.VO.req.DailyAttendanceQueryVO;
import com.youlu.campus.entity.VO.res.DailyAttendanceCalendarDetailRes;
import com.youlu.campus.entity.VO.res.DailyAttendanceCalendarRes;
import com.youlu.campus.entity.VO.res.DailyAttendanceInviteRes;
import com.youlu.campus.entity.domain.UserAttendanceSummary;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.entity.enums.TaskBizType;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.attendance.DailyAttendanceService;
import com.youlu.campus.service.integral.UserIntegralService;
import com.youlu.campus.service.invite.UserInviteRecordService;
import com.youlu.campus.service.visitor.VisitorUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Slf4j
@Service
public class DailyAttendanceServiceImpl implements DailyAttendanceService {
    @Autowired
    private ActivityService activityService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private VisitorUserService visitorUserService;
    @Autowired
    private UserInviteRecordService userInviteRecordService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private UserIntegralService userIntegralService;

    private String userDaKaRedisKey = "com.luyou.user.daka.%s.%s";

    private final String firstDaKa = "firstDaKa";
    // 最近打卡时间
    private final String lastDaKaTime = "lastDaKaTime";

    @Override
    public UserAttendanceSummary getDailyAttendanceDetail(String userId, String activityId) {
        log.info(":>>> 开始获取打卡详情:{},{}", userId, activityId);
        if (StringUtils.isBlank(userId)) {
            log.error(":>>> 用户ID为空");
            throw new BusinessException("用户ID为空");
        }
        Date date = new Date();
        ActivityInfo activityInfo = null;
        if (StringUtils.isBlank(activityId)) {
            log.error(":>>> 活动ID为空,开始查找默认打卡活动ID");
            activityInfo = activityService.findDailyAttendanceActivityInfo();
            if (Objects.isNull(activityInfo)) {
                log.error(":>>> 暂无激活的打卡活动,请联系管理员处理");
                throw new BusinessException("暂无激活的打卡活动,请联系管理员/客服处理");
            }
            log.info(":>>> 默认的活动名:{},活动ID:{}", activityInfo.getName(), activityInfo.getId());
            activityId = activityInfo.getId();
        } else {
            activityInfo = activityService.findOne(activityId);
        }
        UserAttendanceSummary attendanceSummary = new UserAttendanceSummary();
        attendanceSummary.setActivityInfo(activityInfo);
        attendanceSummary.setUserId(userId);
        attendanceSummary.setActivityId(activityInfo.getId());
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId));
        UserParticipateActivityInfo up = mongoTemplate.findOne(query, UserParticipateActivityInfo.class);
        if (Objects.isNull(up)) {
            attendanceSummary.setJoined(Boolean.FALSE);
            return attendanceSummary;
        } else {
            attendanceSummary.setJoined(Boolean.TRUE);
        }
        attendanceSummary.setAdsInfos(activityInfo.getAdsInfos());
        // 开始组装业务逻辑
        ActivityTaskInfo activityTaskInfo = activityService.findActivityTaskInfo(activityId, TaskBizType.MOVEMENT);
        if (Objects.isNull(activityTaskInfo)) {
            log.error(":>>> 活动:{} 邀请任务不存在", activityId);
            throw new BusinessException("活动邀请任务不存在,请联系管理员");
        }
        attendanceSummary.setTaskId(activityTaskInfo.getId());

        // 开始组装业务逻辑
        ActivityTaskInfo activityTaskInviteInfo = activityService.findActivityTaskInfo(activityId, TaskBizType.INVITE);
        if (Objects.isNull(activityTaskInviteInfo)) {
            log.error(":>>> 活动:{} 邀请任务不存在", activityId);
            throw new BusinessException("活动邀请任务不存在,请联系管理员");
        }

        Long invitedCount = userInviteRecordService.getInviteUserCount(userId, activityId, activityTaskInviteInfo.getId(), null);

        attendanceSummary.setInviteUserNum(invitedCount.intValue());
        log.info(":>>> 用户:{},打卡:{},任务:{} 的邀请数量为:{}", userId, activityId, activityTaskInviteInfo.getId(), invitedCount);
        Date dayEnd = TimeUtils.getDayEndTime(date);

        List<UserDayDailyAttendanceInfo> dayDailyAttendanceInfos = findUserDayDailyAttendanceInfo(userId, activityId, activityTaskInfo.getId(), TimeUtils.getDayStartTime(this.getSignUpTime(userId, activityId)), dayEnd);


        Integer dakaDayNum = 0;
        if (CollectionUtils.isEmpty(dayDailyAttendanceInfos)) {
            attendanceSummary.setTotalAttendanceNum(0);
            attendanceSummary.setDayStepNum(0);
            attendanceSummary.setTotalStepNum(0);
            attendanceSummary.setSigned(false);
        } else {
            attendanceSummary.setTotalAttendanceNum(dayDailyAttendanceInfos.size());
            dakaDayNum = dayDailyAttendanceInfos.size();
        }
        int dayNum = TimeUtils.getDiffDays(TimeUtils.getDayStartTime(up.getSignUpTime()), TimeUtils.getDayEndTime(date)) + 1;
        log.info(":>>> 活动开始时间:{} --> 当前时间:{},间隔的天数:{},已经打卡的天数:{}", activityInfo.getStartTime(), date, dayNum, dakaDayNum);
        if (dayNum < 1) {
            // 当天报名
            dayNum = dayNum + 1;
        }
        attendanceSummary.setAbsentNum((dayNum - dakaDayNum) < 1 ? 0 : (dayNum - dakaDayNum));
        // 今日步数
        int daySteps = 0;
        // 累计步数
        int allStep = 0;
        Date dayStart = TimeUtils.getDayStartTime(date);
        for (UserDayDailyAttendanceInfo userDayDailyAttendanceInfo : dayDailyAttendanceInfos) {
            if (userDayDailyAttendanceInfo.getDayTime().after(dayStart) && userDayDailyAttendanceInfo.getDayTime().before(dayEnd)) {
                daySteps = userDayDailyAttendanceInfo.getMovementStep();
                // 标记今日已经打卡
                attendanceSummary.setSigned(true);
            }
            allStep = allStep + userDayDailyAttendanceInfo.getMovementStep();
        }
        log.info(":>>> 时间:{} --->:{}  之间的天数:{}", activityInfo.getStartTime(), date, dayNum);
        // 设置缺卡次数
        if (daySteps < 1) {
            daySteps = visitorUserService.getMovemeStep(userId, new Date());
            log.info(":>>> 用户:{},今日步数为:{}", userId, daySteps);
        }
        attendanceSummary.setDayStepNum(daySteps);
        attendanceSummary.setTotalStepNum(allStep);
        boolean todaySignUp = isAttendance(userId, activityId, date);
        log.info(":>>> 用户:{} 今日是否打卡:{}", userId, todaySignUp);
        attendanceSummary.setSigned(todaySignUp);
        Integer allDayNum = TimeUtils.getDiffDays(activityInfo.getStartTime(), activityInfo.getEndTime());
        Double rate = Double.valueOf(dakaDayNum) / Double.valueOf(allDayNum);
        attendanceSummary.setRate(rate);
        log.info(":>>> 用户的打卡详情:{}", JSON.toJSONString(attendanceSummary));
        return attendanceSummary;
    }

    @Override
    public boolean joinDailyAttendance(String activityId, UserPO userPo) {
        log.info(":>>> 用户报名打卡:{},{}", activityId, JSON.toJSONString(userPo));
        Date date = new Date();
        if (Objects.isNull(userPo)) {
            log.error(":>>> 用户报名打卡参数错误:用户信息为空");
            throw new BusinessException("用户报名打卡参数错误:用户信息为空");
        }
        ActivityInfo activityInfo = null;
        if (StringUtils.isBlank(activityId)) {
            log.error(":>>> 活动ID 为空");
            activityInfo = activityService.findDailyAttendanceActivityInfo();
        } else {
            activityInfo = activityService.findOne(activityId);
        }
        if (Objects.isNull(activityInfo)) {
            log.error(":>>> 打卡活动未配置,请联系管理员");
            throw new BusinessException("暂无激活的打卡活动,请联系管理员/客服处理");
        }
        activityId = activityInfo.getId();
        Query query = new Query().addCriteria(Criteria.where("userId").is(userPo.getId()).and("activityId").is(activityId));
        boolean exist = mongoTemplate.exists(query, UserParticipateActivityInfo.class);
        if (exist) {
            log.warn(":>>>  用户已经报名:{},{}", userPo.getId(), activityId);
            return true;
        }
        UserParticipateActivityInfo userParticipateActivityInfo = new UserParticipateActivityInfo();
        userParticipateActivityInfo.setActivityInfo(activityInfo);
        userParticipateActivityInfo.setActivityId(activityId);
        userParticipateActivityInfo.setUserId(userPo.getId());
        // 已报名状态
        userParticipateActivityInfo.setJoined(true);
        userParticipateActivityInfo.setSignUpTime(date);
        userParticipateActivityInfo.setCreatedTime(date);
        userParticipateActivityInfo.setUniversityName(userPo.getUniversityName());
        userParticipateActivityInfo.setName(userPo.getName());
        userParticipateActivityInfo.setMobile(userPo.getMobile());
        userParticipateActivityInfo.setQq(userPo.getQqNumber());
        userParticipateActivityInfo.setUniversityId(userPo.getUniversityId());
        userParticipateActivityInfo.setProvince(userPo.getProvince());
        userParticipateActivityInfo.setCity(userPo.getCity());
        userParticipateActivityInfo.setAddress(userPo.getFullAddress());
        mongoTemplate.insert(userParticipateActivityInfo);
        // TODO:更新榜单
//        UserPO userPO = visitorUserService.getUser(userPo.getId());
//        // 开始组装业务逻辑
//        ActivityTaskInfo activityTaskInfo = activityService.findActivityTaskInfo(activityId, TaskBizType.INVITE);
//        if (Objects.isNull(activityTaskInfo)) {
//            log.error(":>>> 活动:{} 邀请任务不存在", activityId);
//            throw new BusinessException("活动邀请任务不存在,请联系管理员");
//        }
//        activityService.updateUserInviteStatus(userPO, activityId, activityTaskInfo.getId());
        UserIntegralPO userIntegralPO = new UserIntegralPO();
        userIntegralPO.setUserId(userPo.getId());
        // 签到1积分
        userIntegralPO.setIntegral(1);
        userIntegralPO.setDesc("参与活动获得1国青豆");
        userIntegralPO.setBizId(activityId);
        userIntegralPO.setBizType("activity");
        userIntegralService.addIntegral(userIntegralPO);
        log.info(":>>> 用户:{} 报名打卡活动:{},成功", userPo.getId(), activityId);
        // 更新用户信息
        UserPO userPOUpdate = new UserPO();
        userPOUpdate.setId(userPo.getId());
        userPOUpdate.setName(userPo.getNickName());
        userPOUpdate.setMobile(userPo.getMobile());
        userPOUpdate.setUniversityId(userPo.getUniversityId());
        userPOUpdate.setQqNumber(userPo.getQqNumber());
        userPOUpdate.setProvince(userPo.getProvince());
        userPOUpdate.setCity(userPo.getCity());
        userPOUpdate.setFullAddress(userPo.getFullAddress());
        visitorUserService.updateUser(userPOUpdate);
        return true;
    }

    @Override
    public boolean signUpDailyAttendance(DailyAttendanceQueryVO req) {
        log.info(":>>> 开始打卡:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getUserId()) || StringUtils.isBlank(req.getActivityId())) {
            log.error(":>>> 打卡失败:请求参数错误,活动ID或任务ID为空");
            throw new BusinessException("打卡失败:请求参数错误,活动ID或任务ID为空");
        }
        ActivityInfo activityInfo = activityService.findOne(req.getActivityId());
        Date date = new Date();
        if (date.after(activityInfo.getEndTime())) {
            log.info(":>>> 打卡活动:{} 已经结束:{},当前时间:{}", req.getActivityId(), activityInfo.getEndTime(), date);
            throw new BusinessException("打卡失败:打卡活动已经结束");
        }
        if (date.before(activityInfo.getStartTime())) {
            log.info(":>>> 打卡活动:{} 未开始:{},当前时间:{}", req.getActivityId(), activityInfo.getStartTime(), date);
            throw new BusinessException("打卡失败:打卡活动未开始");
        }
        ActivityTaskInfo taskInfo = activityService.findActivityTaskInfo(req.getActivityId(), TaskBizType.MOVEMENT);
        if (Objects.isNull(taskInfo)) {
            log.error(":>>> 活动:{} ,任务:{} 运动任务不存在,请联系管理员添加", req.getActivityId());
            throw new BusinessException("运动任务不存在,请联系管理员添加");
        }
        Integer step = visitorUserService.getMovemeStep(req.getUserId(), new Date());
        if (step.compareTo(100) == -1) {
            log.error(":>>> 用户:{}, 运动步数为:{}", req.getUserId(), step);
            throw new BusinessException("请运动100步后打卡!");
        }
        log.info(":>>> 打卡任务:{}", JSON.toJSONString(taskInfo));

        // 首次打卡
        if (this.isFirst(req.getUserId(), req.getActivityId())) {
            log.info(":>>> 用户:{} 首次打卡 活动:{}", req.getUserId(), req.getActivityId());
            this.doFirstAttendance(req.getUserId(), req.getActivityId(), new Date());
            ActivityTaskInfo taskInfoInvite = activityService.findActivityTaskInfo(req.getActivityId(), TaskBizType.INVITE);
            if (Objects.isNull(taskInfoInvite)) {
                log.error(":>>> 活动:{} ,任务:{} 邀请任务不存在,请联系管理员添加", req.getActivityId());
                throw new BusinessException("邀请任务不存在,请联系管理员添加");
            }
            List<UserInviteRecord> records = userInviteRecordService.findByInvitedToUserId(req.getUserId(), req.getActivityId(), taskInfoInvite.getId());
            if (!CollectionUtils.isEmpty(records)) {
                log.info(":>>> 活动:{},任务:{},用户:{} 被邀请过", req.getActivityId(), taskInfo.getId(), req.getUserId());
                // 更新邀请状态
                Query queryInviteRecord = new Query().addCriteria(Criteria.where("id").is(records.get(0).getId()));
                Update update = new Update();
                update.set("joinTime", new Date());
                update.set("joinStatus", Boolean.TRUE);
                update.set("updatedTime", new Date());
                mongoTemplate.updateFirst(queryInviteRecord, update, UserInviteRecord.class);
                // 进行补卡
                if (Objects.nonNull(records.get(0).getDayTime())) {
                    // 补卡
                    UserDayDailyAttendanceInfo uR = new UserDayDailyAttendanceInfo();
                    uR.setActivityId(req.getActivityId());
                    uR.setDayTime(TimeUtils.getDayStartTime(records.get(0).getDayTime()));
                    uR.setTaskId(taskInfo.getId());
                    uR.setUserId(records.get(0).getInvitedByUserId());
                    uR.setSignUp(Boolean.TRUE);
                    uR.setSignUpTime(date);
                    uR.setReissue(true);
                    uR.setReissueDesc("邀请用户补卡成功");
                    uR.setReissueType(0);
                    Integer rStep = visitorUserService.getMovemeStep(req.getUserId(), records.get(0).getDayTime());
                    uR.setMovementStep(rStep);

                    uR.setMovementStepTime(records.get(0).getDayTime());
                    uR = mongoTemplate.insert(uR);
                    // 获取QQ运动的步数

                    this.updateQQmovement(uR.getId(), records.get(0).getInvitedByUserId(), req.getActivityId(), taskInfo.getId(), TaskBizType.MOVEMENT, records.get(0).getDayTime());

                    UserIntegralPO userIntegralPO = new UserIntegralPO();
                    userIntegralPO.setUserId(records.get(0).getInvitedByUserId());
                    // 签到1积分
                    userIntegralPO.setIntegral(1);
                    userIntegralPO.setDesc("签到获得1国青豆");
                    userIntegralPO.setBizId(req.getActivityId());
                    userIntegralPO.setBizType("attendance");
                    userIntegralService.addIntegral(userIntegralPO);

                    // TODO:更新邀请榜单
                    UserParticipateActivityTaskInfo userParticipateActivityTaskInfo = new UserParticipateActivityTaskInfo();
                    userParticipateActivityTaskInfo.setActivityId(req.getActivityId());
                    userParticipateActivityTaskInfo.setTaskId(taskInfoInvite.getId());
                    userParticipateActivityTaskInfo.setBizType(TaskBizType.INVITE);
                    userParticipateActivityTaskInfo.setQuantity(1.0D);
                    // 操纵类型0:累加,1:替换
                    // 更新总榜单
                    UserPO invitedByUser = mongoTemplate.findOne(new Query().addCriteria(Criteria.where("id").is(records.get(0).getInvitedByUserId())), UserPO.class);

                    activityService.updateUserTaskAllRanking(invitedByUser, userParticipateActivityTaskInfo,
                            0);
                    // 更新周榜单
                    activityService.updateUserTaskWeekRanking(invitedByUser, userParticipateActivityTaskInfo,
                            0);

                }
            }
        }
        // Redis 打卡
        this.attendance(req.getUserId(), req.getActivityId(), date);
        Query query = new Query().addCriteria(Criteria.where("userId").is(req.getUserId()).and("activityId").is(req.getActivityId()).and("taskId").is(taskInfo.getId()));
        Date dayStart = TimeUtils.getDayStartTime(date);
        Date dayEnd = TimeUtils.getDayEndTime(date);
        Criteria criteria = new Criteria().andOperator(Criteria.where("dayTime").gte(TimeUtils.dateToISODate(dayStart)), Criteria.where("dayTime").lte(TimeUtils.dateToISODate(dayEnd)));
        query.addCriteria(criteria);
        UserDayDailyAttendanceInfo u = mongoTemplate.findOne(query, UserDayDailyAttendanceInfo.class);
        if (Objects.isNull(u)) {
            log.error(":>>> 用户:{} 日打卡信息不存在,添加打卡", req.getUserId());
            u = new UserDayDailyAttendanceInfo();
            u.setActivityId(req.getActivityId());
            u.setDayTime(dayStart);
            u.setTaskId(taskInfo.getId());
            u.setUserId(req.getUserId());
            u.setSignUp(Boolean.TRUE);
            u.setSignUpTime(date);
            u.setMovementStep(step);
            u.setMovementStepTime(date);
            u = mongoTemplate.insert(u);
            // 获取QQ运动的步数
            this.updateQQmovement(u.getId(), req.getUserId(), req.getActivityId(), taskInfo.getId(), TaskBizType.MOVEMENT, date);
            UserIntegralPO userIntegralPO = new UserIntegralPO();
            userIntegralPO.setUserId(req.getUserId());
            // 签到1积分
            userIntegralPO.setIntegral(1);
            userIntegralPO.setDesc("签到获得1国青豆");
            userIntegralPO.setBizId(req.getActivityId());
            userIntegralPO.setBizType("attendance");
            userIntegralService.addIntegral(userIntegralPO);
            return true;
        } else {
            if (Boolean.TRUE.equals(u.getSignUp())) {
                log.error(":>>> 用户:{} 已经打卡,不要重复打卡", req.getUserId(), req.getActivityId());
                return true;
            } else {
                Query updateQ = new Query().addCriteria(Criteria.where("id").is(u.getId()));
                Update update = new Update();
                update.set("signUp", Boolean.TRUE);
                update.set("signUpTime", date);
                mongoTemplate.updateFirst(updateQ, update, UserDayDailyAttendanceInfo.class);
                // 获取QQ运动的步数
                this.updateQQmovement(u.getId(), req.getUserId(), req.getActivityId(), taskInfo.getId(), TaskBizType.MOVEMENT, date);

                UserIntegralPO userIntegralPO = new UserIntegralPO();
                userIntegralPO.setUserId(req.getUserId());
                // 签到1积分
                userIntegralPO.setIntegral(1);
                userIntegralPO.setDesc("签到获得1国青豆");
                userIntegralPO.setBizId(req.getActivityId());
                userIntegralPO.setBizType("attendance");
                userIntegralService.addIntegral(userIntegralPO);
                return true;
            }
        }
    }

    @Override
    @Async
    public void updateQQmovement(String id, String userId, String activityId, String taskId, TaskBizType taskBizType, Date date) {
        log.info(":>>> 开始更新QQ运动,userId:{},activityId:{},taskId:{},taskBizType:{},date:{}", userId, activityId, taskId, taskBizType, date);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(activityId) || StringUtils.isBlank(taskId) ||
                Objects.isNull(taskBizType) || Objects.isNull(date)) {
            log.error(":>>> 更新QQ运动参数错误:");
            return;
        }
        try {
            Integer step = visitorUserService.getMovemeStep(userId, date);
            log.info(":>>> 用户:{} QQ运动的步数:{}", userId, step);
            Query query = new Query().addCriteria(Criteria.where("id").is(id));
            Update update = new Update();
            update.set("movementStep", step);
            update.set("movementStepTime", date);
            update.set("updatedTime", date);
            mongoTemplate.updateFirst(query, update, UserDayDailyAttendanceInfo.class);
            UserPO userPO = visitorUserService.getUser(userId);
            // TODO:更新榜单
            UserParticipateActivityTaskInfo taskInfo = new UserParticipateActivityTaskInfo();
            taskInfo.setUserId(userId);
            taskInfo.setActivityId(activityId);
            taskInfo.setTaskId(taskId);
            taskInfo.setQuantity(Double.valueOf(step+""));
            taskInfo.setBizType(taskBizType);
            activityService.updateUserTaskWeekRanking(userPO, taskInfo, 0);
            activityService.updateUserTaskAllRanking(userPO, taskInfo, 0);


        } catch (Exception e) {
            log.error(":>>> 更新QQ运动错误:{}", e.getMessage());
        }

    }

    @Override
    public List<UserDayDailyAttendanceInfo> findUserDayDailyAttendanceInfo(String userId, String activityId, String taskId, Date startTime, Date endTime) {
        log.info(":>>> 开始获取用户每日打卡集合:用户:{},活动:{},任务:{},开始时间:{},结束时间:{}", userId, activityId, taskId, startTime, endTime);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(activityId) || StringUtils.isBlank(taskId)
                || Objects.isNull(startTime) || Objects.isNull(endTime)) {
            log.error(":>>> 获取每日打卡参数错误");
            throw new BusinessException("获取每日打卡参数错误");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        Criteria criteria = new Criteria().andOperator(Criteria.where("dayTime").gte(TimeUtils.dateToISODate(startTime)), Criteria.where("dayTime").lte(TimeUtils.dateToISODate(endTime)));
        query.addCriteria(criteria);
        query.addCriteria(Criteria.where("activityId").is(activityId).and("taskId").is(taskId));
//        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "dayTime")));
        Sort sort=Sort.by(Sort.Direction.ASC, "dayTime");
        query.with(sort);
        List<UserDayDailyAttendanceInfo> infos = mongoTemplate.find(query, UserDayDailyAttendanceInfo.class);
        return infos;
    }

    @Override
    public DailyAttendanceCalendarRes getDailyAttendanceCalendar(Date startTime, Date endTime, String activityId,
                                                                 String taskId, String userId) {
        log.info(":>>> 开始获取用户打卡日历:startTime:{},endTime:{},activityId:{},taskId:{},userId:{}", startTime, endTime, activityId, taskId, userId);
        if (Objects.isNull(startTime) || Objects.isNull(endTime) || StringUtils.isBlank(activityId) || StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 获取打卡日历参数错误");
            throw new BusinessException("获取打卡日历参数错误");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId).and("taskId").is(taskId));
        Criteria criteria = new Criteria().andOperator(Criteria.where("dayTime").gte(TimeUtils.dateToISODate(startTime)), Criteria.where("dayTime").lte(TimeUtils.dateToISODate(endTime)));
        query.addCriteria(criteria);
//        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "dayTime")));
        Sort sort=Sort.by(Sort.Direction.ASC, "dayTime");
        query.with(sort);
        List<UserDayDailyAttendanceInfo> datas = mongoTemplate.find(query, UserDayDailyAttendanceInfo.class);
        if (Objects.isNull(datas)) {
            datas = new ArrayList<UserDayDailyAttendanceInfo>();
        }
        List<UserDayDailyAttendanceInfo> results = new ArrayList<>();
        List<Date> monthDays = TimeUtils.getTimeList(TimeUtils.getYear(startTime), TimeUtils.getMonth(startTime) - 1, 1);

        int no = 1;
        HashMap<String, Boolean> hashMap = new HashMap<>();
        String dStr = "";
        log.info(":>>> 打卡的原始数据:{}", JSON.toJSONString(datas));
        for (UserDayDailyAttendanceInfo userDayDailyAttendanceInfo : datas) {
            if (Objects.nonNull(userDayDailyAttendanceInfo.getDayTime())) {
                dStr = TimeUtils.parse(userDayDailyAttendanceInfo.getDayTime(), "yyyy/MM/dd");
                if (Boolean.TRUE.equals(userDayDailyAttendanceInfo.getSignUp())) {
                    log.info(":>>> 日期:{},已经打卡", dStr);
                    hashMap.put(dStr, Boolean.TRUE);
                }
            }
        }
        String ddStr = "";
        Date signUpTime = this.getSignUpTime(userId, activityId);
        Date date = new Date();
        for (Date d : monthDays) {

            UserDayDailyAttendanceInfo ud = new UserDayDailyAttendanceInfo();
            ud.setDayTime(d);
            ud.setUserId(userId);
            ud.setTaskId(taskId);
            ud.setActivityId(activityId);
            ddStr = TimeUtils.parse(d, "yyyy/MM/dd");
            // 未报名之前
            if (d.before(TimeUtils.getDayStartTime(signUpTime))) {
                ud.setSignUpType("disable");
                ud.setSignUp(Boolean.FALSE);
            } else if (d.after(TimeUtils.getDayEndTime(date))) {
                ud.setSignUpType("pending");
                ud.setSignUp(Boolean.FALSE);
            } else {
                if (hashMap.containsKey(ddStr)) {
                    ud.setSignUp(Boolean.TRUE);
                    ud.setSignUpType("ok");
                    log.info(":.>> 日期:{} 标记为打卡", ddStr);
                } else {
                    ud.setSignUpType("no");
                    log.info(":.>> 日期:{} 标记为缺卡", ddStr);
                }
            }
            ud.setNo(no);
            no++;
            ud.setDayTimeStr(ddStr);
            results.add(ud);
        }

        DailyAttendanceCalendarRes re = new DailyAttendanceCalendarRes();
        re.setDatas(results);
        log.info(":>>> 日历数据详情:{}", JSON.toJSONString(re));
        return re;
    }

    @Override
    public DailyAttendanceCalendarDetailRes getDailyAttendanceCalendarDetails(String userId, String activityId, String taskId, Date currentTime) {
        log.info(":>>> 获取打卡日历详情,userId:{},activityId:{},taskId:{},currentTime:{}", userId, activityId, taskId, currentTime);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(activityId) ||
                StringUtils.isBlank(taskId) || Objects.isNull(currentTime)) {
            log.error(":>>> 获取日历打卡详情参数错误");
            throw new BusinessException("获取日历打卡详情参数错误");
        }
        ActivityInfo activityInfo = activityService.findOne(activityId);
        if (Objects.isNull(activityInfo)) {
            log.error(":>>> 活动:{} 不存在", activityId);
            throw new BusinessException("活动不存在");
        }
        DailyAttendanceCalendarDetailRes d = new DailyAttendanceCalendarDetailRes();
        Date dayEndTime = TimeUtils.getDayEndTime(currentTime);

        Date signUpTime = this.getSignUpTime(userId, activityId);
        int dayNum = TimeUtils.getDiffDays(TimeUtils.getDayStartTime(signUpTime), dayEndTime);
        log.info(":>>> 报名时间:{} 天的结束时间为:{},活动已经持续的天数:{}", signUpTime, dayEndTime, dayNum);
        // 总数据
        List<UserDayDailyAttendanceInfo> dayDAInfos = this.findUserDayDailyAttendanceInfo(userId, activityId, taskId, TimeUtils.getDayStartTime(signUpTime), dayEndTime);

        if (CollectionUtils.isEmpty(dayDAInfos)) {
            log.info(":>>> 用户:{} 日打卡数据不存在", userId);
            d.setAbsentDAMonthDays(dayNum);
            d.setTodayAbsent(false);
            d.setTodaySteps(0);
            d.setTotalDADays(0);
            d.setTotalDAMonthDays(0);
            d.setTotalDANonStopMonthDays(0);
            d.setTotalMovementMonthSteps(0);
            d.setTotalMovementSteps(0);
            d.setTotalStepRanking(1001);
            d.setWeekStepRanking(1001);
            d.setWeekSteps(0);
            d.setAdsInfos(activityInfo.getAdsInfos());
            log.info(":>>> 用户:{} 的打卡数据为:{}", userId, JSON.toJSONString(d));
            return d;
        }
        // 累计步数
        int allSteps = 0;
        // 月累计打卡天数
        int totalMonthAdDays = 0;
        // 月连续打卡天数
        int totalMonthNotStepAdDays = 0;
        // 月累计打卡步数
        int totalMonthAdSteps = 0;
        // 月缺卡天数
        int absentMonthAdSteps = 0;
        // 连续打卡标记
        boolean nonStop = true;
        // 周累计打卡步数
        int totalWeekAdSteps = 0;
        // 本月开始时间
        Date startMonth = TimeUtils.getBeginDayOfMonth();
        // 本月结束时间
        Date endMonth = TimeUtils.getEndDayOfMonth();
        // 本周开始时间
        Date startWeek = TimeUtils.getBeginDayOfWeek();
        // 本周结束时间
        Date endWeek = TimeUtils.getEndDayOfWeek();
        // 当日开始时间
        Date startDayTime = TimeUtils.getDayStartTime(currentTime);
        // 今日是否缺卡，默认为否
        boolean todayAbsent = false;

        log.info(":>>> 本月开始时间:{},本月结束时间:{};本周开始时间:{},本周结束时间:{};当日开始时间:{}", startMonth, endMonth, startWeek, endWeek, startDayTime);
        d.setTodayAbsent(todayAbsent);
        d.setTodaySteps(0);
        Date preDay = null;
        List<Integer> nonStopList = new ArrayList<>();
        //本月活动开始时间
        Date sMonthTime = TimeUtils.max(TimeUtils.max(activityInfo.getStartTime(), signUpTime), startMonth);
        for (UserDayDailyAttendanceInfo udda : dayDAInfos) {
            log.info(":>>> 日期:{},步数:{}", udda.getDayTime(), udda.getMovementStep());
            allSteps = allSteps + (Objects.isNull(udda.getMovementStep()) == true ? 0 : udda.getMovementStep());
            if (TimeUtils.between(startDayTime, dayEndTime, currentTime)) {
                log.info(":>>> 今日:{},已经打卡,标记不缺卡,步数:{}", udda.getMovementStep());
                d.setTodayAbsent(true);
                d.setTodaySteps(udda.getMovementStep());
            }
            if (TimeUtils.between(TimeUtils.getDayStartTime(sMonthTime), endMonth, udda.getDayTime())) {
                totalMonthAdDays = totalMonthAdDays + 1;
                totalMonthAdSteps = totalMonthAdSteps + udda.getMovementStep();
            }
            if (udda.getDayTime().after(TimeUtils.getDayStartTime(signUpTime)) && TimeUtils.between(startWeek, endWeek, udda.getDayTime())) {
                totalWeekAdSteps = totalWeekAdSteps + udda.getMovementStep();
            }
            if (nonStop && TimeUtils.isNotStop(preDay, udda.getDayTime())) {
                totalMonthNotStepAdDays = totalMonthNotStepAdDays + 1;
            } else {
                nonStop = false;
                nonStopList.add(totalMonthNotStepAdDays);
                totalMonthNotStepAdDays = 0;

            }
        }
        Integer AbsentDAMonthDays = TimeUtils.getDiffDays(sMonthTime, TimeUtils.getDayEndTime(new Date())) - totalMonthAdDays + 1;
        d.setAbsentDAMonthDays(AbsentDAMonthDays < 0 ? 0 : AbsentDAMonthDays);
        d.setTotalDADays(dayDAInfos.size());
        d.setTotalDAMonthDays(totalMonthAdDays);
        if (!CollectionUtils.isEmpty(nonStopList)) {
            d.setTotalDANonStopMonthDays(Collections.max(nonStopList));
        } else {
            d.setTotalDANonStopMonthDays(totalMonthNotStepAdDays);
        }
        d.setTotalMovementMonthSteps(totalMonthAdSteps);
        d.setTotalMovementSteps(allSteps);
        RankingListQueryVO queryVO = new RankingListQueryVO();
        queryVO.setActivityId(activityId);
        queryVO.setTime("all");
        queryVO.setTaskId(taskId);
        queryVO.setUserId(userId);
        Integer totalOrder = activityService.getUserTaskRankingOrderNo(queryVO);
        queryVO.setTime("week");
        Integer weekOrder = activityService.getUserTaskRankingOrderNo(queryVO);
        d.setTotalStepRanking(totalOrder);
        d.setWeekStepRanking(weekOrder);
        d.setWeekSteps(totalWeekAdSteps);
        d.setAdsInfos(activityInfo.getAdsInfos());
        Integer daySteps = visitorUserService.getMovemeStep(userId, new Date());
        d.setTodaySteps(daySteps);
        log.info(":>>> 打卡日历详情:{}", JSON.toJSONString(d));
        return d;
    }

    @Override
    public String reissueAd(String userId, String activityId, String taskId, String adsInfoId, Date date) {
        log.info(":>>> 观看广告补卡:{},{},{},{},{}", userId, activityId, taskId, adsInfoId, date);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(activityId) || StringUtils.isBlank(taskId) ||
                StringUtils.isBlank(adsInfoId) || Objects.isNull(date)) {
            log.error(":>>> 观看广告补卡参数错误");
            throw new BusinessException("观看广告补卡参数错误");
        }
        Date dayStart = TimeUtils.getDayStartTime(date);
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId).and("taskId").is(taskId));
        Criteria criteria = new Criteria().andOperator(Criteria.where("dayTime").gte(TimeUtils.dateToISODate(dayStart)), Criteria.where("dayTime").lte(TimeUtils.dateToISODate(TimeUtils.getDayEndTime(date))));
        query.addCriteria(criteria);
        boolean exist = mongoTemplate.exists(query, UserDayDailyAttendanceInfo.class);
        if (exist) {
            log.info(":>>> 用户:{} 已经打卡,不需要补卡", userId);
            throw new BusinessException("已经打卡,不需要补卡");
        }
        UserAdsRecord userAdsRecord = new UserAdsRecord();
        userAdsRecord.setActivityId(activityId);
        userAdsRecord.setAdsInfoId(adsInfoId);
        userAdsRecord.setStartTime(date);
        userAdsRecord.setTaskId(taskId);
        userAdsRecord.setUserId(userId);
        userAdsRecord.setCreatedTime(date);
        userAdsRecord.setDayTime(dayStart);
        userAdsRecord = mongoTemplate.insert(userAdsRecord);
        return userAdsRecord.getId();
    }

    @Override
    public boolean reissueAdCallBack(String id) {
        log.info(":>>> 观看广告补卡:{}", id);
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 观看广告补卡错误:ID为空");
            throw new BusinessException("观看广告补卡错误:ID为空");
        }
        Query queryR = new Query().addCriteria(Criteria.where("id").is(id));
        UserAdsRecord userAdsRecord = mongoTemplate.findOne(queryR, UserAdsRecord.class);
        if (Objects.isNull(userAdsRecord)) {
            log.error(":>>> 观看广告记录不存在:{}", id);
            throw new BusinessException("观看广告记录不存在");
        }
        Date dayStart = TimeUtils.getDayStartTime(userAdsRecord.getDayTime());
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userAdsRecord.getUserId()).and("activityId").is(userAdsRecord.getActivityId()).and("taskId").is(userAdsRecord.getTaskId()));
        Criteria criteria = new Criteria().andOperator(Criteria.where("dayTime").gte(TimeUtils.dateToISODate(dayStart)), Criteria.where("dayTime").lte(TimeUtils.dateToISODate(TimeUtils.getDayEndTime(userAdsRecord.getDayTime()))));
        query.addCriteria(criteria);
        boolean exist = mongoTemplate.exists(query, UserDayDailyAttendanceInfo.class);
        if (exist) {
            log.info(":>>> 用户:{} 已经打卡,不需要补卡", userAdsRecord.getUserId());
            return true;
        }
        // 补卡代码
        Date date = new Date();
        UserDayDailyAttendanceInfo insert = new UserDayDailyAttendanceInfo();
        insert.setSignUp(true);
        insert.setActivityId(userAdsRecord.getActivityId());
        insert.setTaskId(userAdsRecord.getTaskId());
        insert.setUserId(userAdsRecord.getUserId());
        insert.setDayTime(dayStart);
        insert.setSignUpTime(date);
        insert.setReissue(true);
        insert.setReissueDesc("观看广告");
        insert.setReissueTime(date);
        //补卡类型,0:邀请,1:广告,2:扣减积分,3:现金
        insert.setReissueType(1);
        insert.setBizId(userAdsRecord.getAdsInfoId());
        insert.setDuration(TimeUtils.dateDiff(date, userAdsRecord.getStartTime()));
        insert = mongoTemplate.insert(insert);
        // 获取QQ运动的步数
        this.updateQQmovement(insert.getId(), userAdsRecord.getUserId(), userAdsRecord.getActivityId(),
                userAdsRecord.getTaskId(), TaskBizType.MOVEMENT, date);
        return true;
    }

    @Override
    public DailyAttendanceInviteRes dailyAttendanceInviteDetail(String userId, String activityId,
                                                                String taskId) {
        log.info(":>>> 开始获取打卡邀请:{},{},{}", userId, activityId, taskId);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(activityId) || StringUtils.isBlank(taskId)) {
            log.error(":>>> 获取打卡详情参数错误");
            throw new BusinessException("获取打卡详情参数错误");
        }
        DailyAttendanceInviteRes res = new DailyAttendanceInviteRes();
        Query query = new Query().addCriteria(Criteria.where("invitedByUserId").is(userId).and("activityId").is(activityId).and("taskId").is(taskId));
        List<UserInviteRecord> records = mongoTemplate.find(query, UserInviteRecord.class);
        if (!CollectionUtils.isEmpty(records)) {
            res.setInviteNum(records.size());
        }
        ActivityTaskInfo taskInfo = activityService.findActivityTaskInfo(taskId);
        if (Objects.isNull(taskInfo)) {
            log.error(":>>> 活动任务不存在:{}", taskId);
            throw new BusinessException("活动任务不存在");
        }
        res.setImageUrl(taskInfo.getRewardImageUrl());
        if (StringUtils.isNotBlank(taskInfo.getRewardImageUrl())) {
            res.getImageUrls().add(taskInfo.getRewardImageUrl());
        }
        return res;
    }

    @Override
    public void attendance(String userId, String activityId, Date date) {
        log.info(":>>> 用户:{} 开始打卡:{},{}", userId, activityId, date);

        String userKey = String.format(userDaKaRedisKey, userId, activityId);
        String dateStr = TimeUtils.parse(date, "yyyy-MM-dd");
        redisTemplate.opsForHash().put(userKey, dateStr, date);
    }

    @Override
    public boolean isAttendance(String userId, String activityId, Date date) {
        log.info(":>>> 开始判断用户:{},活动:{},日期:{},是否打卡", userId, activityId, date);
        String userKey = String.format(userDaKaRedisKey, userId, activityId);
        String dateStr = TimeUtils.parse(date, "yyyy-MM-dd");
        try {
            return redisTemplate.opsForHash().hasKey(userKey, dateStr);
        } catch (Exception e) {
            log.error(":>>> 判断用户:{},{},{} 是否打卡错误:{}", userId, activityId, date, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean isFirst(String userId, String activityId) {
        log.info(":>>> 开始判断用户是否首次打卡用户ID:{},活动ID:{}", userId, activityId);
        String userKey = String.format(userDaKaRedisKey, userId, activityId);
        boolean e = redisTemplate.opsForHash().hasKey(userKey, firstDaKa);
        log.info(":>>> 用户:{},活动:{},是否是首次打卡", userId, activityId);
        return e;
    }

    @Override
    public boolean doFirstAttendance(String userId, String activityId, Date date) {
        log.info(":>>> 开始设置首次打卡标记userId:{},activityId:{},date:{}", userId, activityId, date);
        String userKey = String.format(userDaKaRedisKey, userId, activityId);
        boolean r = redisTemplate.opsForHash().putIfAbsent(userKey, firstDaKa, date);
        log.info(":>>> 用户:{},活动:{},设置首次打卡标记", userId, activityId, r);
        return r;
    }

    /**
     * 获取QQ运动步数
     *
     * @param userId
     * @return
     */
    private Integer getQQMovement(String userId, Date date) {
        Integer step = visitorUserService.getMovemeStep(userId, date);
        log.info(":>>> 用户:{},{}--> 步数:{}", userId, date, step);
        if (Objects.isNull(step) || step.compareTo(0) != 1) {
            return randomStep();
        }
        return step;
    }

    private Integer randomStep() {

        int max = 2000, min = 50;
        return (int) (Math.random() * (max - min) + min);
    }

    /**
     * 获取用户报名时间
     *
     * @param userId
     * @param activityId
     * @return
     */
    private Date getSignUpTime(String userId, String activityId) {
        Query query = new Query().addCriteria(Criteria.where("userId").is(userId).and("activityId").is(activityId));
        UserParticipateActivityInfo up = mongoTemplate.findOne(query, UserParticipateActivityInfo.class);
        return up.getSignUpTime();
    }

}
